#include "topQueriesProcessor.h"
#include <iostream>
#include <fstream>
#include <vector>
#include <iterator>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
TopQueriesProcessor::TopQueriesProcessor( ):intervalCount_(0), startTime_(0), initializedStartTime_(false),
	outputFormat_(PV_ALL), minFrequency_( 0 )
{
	
}

TopQueriesProcessor::~TopQueriesProcessor() 
{
}

void TopQueriesProcessor::setInterval( const uint32_t &interval )
{
	interval_ = interval;
}

uint32_t TopQueriesProcessor::interval( ) const
{
	return interval_;
}

void TopQueriesProcessor::setOutfile( const std::string &outfile )
{
	outfile_ = outfile;
}

std::string TopQueriesProcessor::outfile( ) const
{
	return outfile_;
}

void TopQueriesProcessor::processDNSPacket( const Packet &p )
{
	if( p.appSize() && p.appIs< DNS >() )
	{
		std::string source;
		if( p.inetSize() && p.inetIs< IPv4 >() )
		{
			IPv4 ip = p.getInet<IPv4>();
			uint32_t address = ip.sourceAddress();
			source = inet_ntoa( (*(struct in_addr*)&address));
		}
	  DNS dns = p.getApp<DNS>( );
		if( dns.opcodeFlag() == dnsopcode::query ) //don't need to count responses
		{
			if( !initializedStartTime_ )
			{
				startTime_ = p.timestamp().tv_sec;
				initializedStartTime_ = true;
			}
			time_t ts = p.timestamp().tv_sec - startTime_;
			uint32_t interval = ts / interval_; 
			if( interval+1 > intervalCount_ )
				intervalCount_ = interval+1; //keeps track of the number of intervals
			for( uint32_t i = 0; i < dns.numberOfQuestions(); ++i )
			{
				std::string query = dns.question( i ).queryName();
				uint32_t size = queryCounts_[query].size();
				if( size < interval + 1) //updateSize
				{
					for( uint32_t i = 0; i < interval - size + 1; ++i )
						queryCounts_[query].push_back( 0 );
				}
				queryCounts_[query][interval] += 1;
				queryClients_[query][source] += 1;
			}
		}
	}
}

void  TopQueriesProcessor::printResults() const 
{
	std::map< std::string, std::deque<uint32_t> >::const_iterator itr;
	uint32_t highest = 0; 
	std::string key = "";
	for( itr = queryCounts_.begin(); itr != queryCounts_.end(); ++itr )
	{
		uint32_t total = 0;
		for( uint32_t i = 0; i < itr->second.size(); ++i )
		{
			total += itr->second[i];
		}
		if( total > highest )
		{
			highest = total;
			key = itr->first;
		}
	}

	std::cout << key << " was queried " << highest << " times. " << std::endl;

}

void TopQueriesProcessor::writeResults( )
{
	switch( outputFormat_ )
	{
		case JIT:
			writeJitFile();
			break;
		case PV_ALL:
			writePvAll();
			break;
		case PV_2_LEVEL:
			writePv2Level();
			break;
	};
}

void TopQueriesProcessor::writePv2Level()
{
	std::map< std::string, std::deque< uint32_t > > filteredData = filter( queryCounts_ );
	if( filteredData.size() > 0 ) //if there are no queries then there is nothing to do
	{
		std::ofstream file;
		file.open( outfile_.c_str() );
	

		file.close();
	}
}

void TopQueriesProcessor::writePvAll()
{
	std::map< std::string, std::deque< uint32_t > > filteredData = filter( queryCounts_ );
	if( filteredData.size() > 0 ) //if there are no queries then there is nothing to do
	{
		std::ofstream file;
		file.open( outfile_.c_str() );

		file << "var domains = { \n"; 

		typedef std::map< std::string, std::deque<uint32_t> >::const_iterator ConstItr;
		for( ConstItr itr = filteredData.begin(); itr != filteredData.end(); ++itr )
		{
			file << "\"" << itr->first << "\": {\n";
			file << "requests: [";
			uint32_t i; //we need i after loop
			for( i = 0; i < itr->second.size(); ++i )
			{
				file << itr->second[i];
				if( i + 1 < intervalCount_ )
				{
					file << ",";
				}
				else
				{
					file << "],\n";
				}
			}
			for( ; i < intervalCount_; ++i )
			{
				file << 0;
				if( i + 1 < intervalCount_ )
				{
					file << ",";
				}
				else
				{
					file << "]\n";
				}
			}
//			file << "uniqueSources: [" << queryClients_[itr->first].size() << "]" << std::endl;
			file << "}";
			if( std::distance(itr, ConstItr(filteredData.end())) <= 1 ) //i.e. this is the last element
			{
				file << std::endl << "};" << std::endl;
			}
			else
			{
				file << ",\n";
			}
		}
		file.close();
	}
}

void TopQueriesProcessor::writeJitFile( )
{
	std::map< std::string, std::deque< uint32_t > > filteredData = filter( queryCounts_ );
	if( filteredData.size() > 0 ) //if there are no queries then there is nothing to do
	{
		std::ofstream file;
		file.open( outfile_.c_str() );
		std::vector< std::string > labels;

		std::map< std::string, std::deque<uint32_t> >::const_iterator itr;
		for( itr = filteredData.begin(); itr != filteredData.end(); ++itr )
		{
			labels.push_back( itr->first );
		}

		file << "var json = {" << std::endl;
		file << "\t'label': [";
		for( uint32_t i = 0; i < labels.size(); ++i )
		{
			file <<"'" << labels[i];
			if( i + 1 < labels.size() )
			{ 
				file << "', ";
			}
			else
			{
				file << "'],\n";
			}
		}

		file << "\t'values': [\n";
		for( uint32_t i = 0; i < filteredData[ labels[0] ].size(); ++i )
		{ //iterates the intervals
			file << "{\n"; 
			file << "'label': '" << i * interval_ << " - " << (i*interval_) + (interval_ - 1) << " sec',\n";
			file << "'values': [";
			for( uint32_t k = 0; k < labels.size(); ++k )
			{
				if( i < filteredData[ labels[k] ].size() )
					file << filteredData[ labels[k] ][i]; 
				else
					file << 0; 
				if( k + 1 < labels.size() )
				{
					file << ", "; 
				}
				else
				{
					file << "]\n";
				}
			}
			if( i + 1 < filteredData[ labels[0] ].size() )
			{
				file << "},\n";
			}
			else
			{
				file << "}]\n";
			}
		}
		file << "};" << std::endl;
		file.close();
	}
}

std::map< std::string, std::deque<uint32_t> > TopQueriesProcessor::filter( const std::map< std::string, std::deque< uint32_t> > &rawdata ) const
{
	if( minFrequency_ == 0 || rawdata.size() == 0 )
	{
		return rawdata;
	}

	std::map< std::string, std::deque< uint32_t > > filteredData;
	for( uint32_t i = 0; i < intervalCount_;  ++i )
	{
		filteredData[ "filtered" ].push_back( 0 );
	}

	std::map< std::string, std::deque< uint32_t > >::const_iterator itr;
	for( itr = rawdata.begin(); itr != rawdata.end(); ++itr )
	{
		bool include = false;
		for( uint32_t i = 0; i < itr->second.size(); ++i )
		{
			if( (itr->second)[i] >= minFrequency_ )
			{
				include = true;
				break;
			}
		}
		if( include )
		{
			filteredData[ itr->first ] = itr->second;
		}
		else
		{
			for( uint32_t i = 0; i < itr->second.size(); ++i )
			filteredData[ "filtered" ][i] += itr->second[i];
		}
	}

	return filteredData;
}

void TopQueriesProcessor::setMinFrequency( const uint32_t &minFrequency )
{
	minFrequency_ = minFrequency;
}

uint32_t TopQueriesProcessor::minFrequency() const 
{
	return minFrequency_;
}
